/**
 * Trivial function, casts an Int to a Float.
 */
function f(i: Int): Float {
    return i;
}

/**
 * Takes f + its arg and applies.
 */
function g(func: function (Int) -> Float, i: Int): Float {
    f(1);
    return func(i);
}

/**
 * Varargs test.
 */
function k(func: function (CString, args...) -> Void, s: CString) {
    func(s, 1, 2, 3);
}

function l(s: CString, args...) {
    printf("hello, %s\n", s);
}

function withArgDefaults(i: Int, j: Float = 2): Float {
    return j;
}

struct MyStruct {
    var a: Int;

    /**
     * Nested function types on a static method. Calls g, which calls f.
     */
    public static function h(func: function (function (Int) -> Float, Int) -> Float, func2: function (Int) -> Float, i: Int): Float {
        return func(func2, i);
    }

    /**
     * Same as above but inferring the function types.
     */
    public static function j(func, func2, i: Int): Float {
        return func(func2, i);
    }
}

function main() {
    printf("%.1f\n", MyStruct.h(g, f, 4));
    printf("%.1f\n", MyStruct.j(g, f, 6));
    k(l, "world");
    printf("%.1f\n", withArgDefaults(1));
    printf("%.1f\n", withArgDefaults(1, 3.0));
}
